/////////////////////////////////////////////////////////////////////////////
// Name:        ax_ui_adaptation.h
// Purpose:     Adapts controls to self-voicing
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"
#include "wx/module.h"
#include "wx/tipwin.h"

#include "ax/ax_ui_adaptation.h"
#include "ax/ax_ui_adaptation_standard.h"
#include "ax/ax_speech.h"
#include "ax/ax_utils.h"
#include "ax/ax_textctrl.h"
#include "ax/ax_view.h"
#include "ax/ax_help_provider.h"
#include "shortcutcust/shortcutcust.h"

IMPLEMENT_CLASS(AxAdapter, wxEvtHandler)

BEGIN_EVENT_TABLE(AxAdapter, wxEvtHandler)
    EVT_CHAR(AxAdapter::OnChar)
    EVT_KEY_DOWN(AxAdapter::OnKeyDown)
    EVT_IDLE(AxAdapter::OnIdle)
    EVT_SET_FOCUS(AxAdapter::OnSetFocus)
    EVT_SYS_COLOUR_CHANGED(AxAdapter::OnSysColourChanged)

    // ToDo: use Connect so we can connect custom shortcuts

    EVT_MENU(AxGetId(AxIdentifierDescribeContext),          AxAdapter::OnDescribeContext)
    EVT_MENU(AxGetId(AxIdentifierDescribeContainerWindow),  AxAdapter::OnDescribeContainer)
    EVT_MENU(AxGetId(AxIdentifierStopSpeech),               AxAdapter::OnStopSpeech)
    EVT_MENU(AxGetId(AxIdentifierPauseResumeSpeech),        AxAdapter::OnPauseResumeSpeech)
    EVT_MENU(AxGetId(AxIdentifierSkipForward),              AxAdapter::OnSkipForward)
    EVT_MENU(AxGetId(AxIdentifierSkipBack),                 AxAdapter::OnSkipBack)
    EVT_MENU(AxGetId(AxIdentifierGoHome),                   AxAdapter::OnGoHome)
    EVT_MENU(AxGetId(AxIdentifierGoUp),                     AxAdapter::OnGoUp)
    //EVT_MENU(AxGetId(AxIdentifierContextHelp),              AxAdapter::OnContextHelp)
END_EVENT_TABLE()

AxAdapter::AxAdapter(wxWindow* window, AxSpeech* speech)
{
    Init();

    m_window = window;

    if (speech)
        m_speech = speech;
    else if (AxViewManager::Get())
        m_speech = & AxViewManager::Get()->GetSpeech();

    if (AxViewManager::Get())
        m_shortcutManager = AxViewManager::Get()->GetShortcutManager();
}

AxAdapter::~AxAdapter()
{
}

void AxAdapter::Init()
{
    m_window = NULL;
    m_speech = NULL;
    m_channel = AxSpeechMainChannel;
    m_shortcutManager = NULL;
}

void AxAdapter::OnKeyDown(wxKeyEvent& event)
{
    if (m_shortcutManager && m_window)
    {
        if (event.GetKeyCode() == WXK_SHIFT || event.GetKeyCode() == WXK_CONTROL || event.GetKeyCode() == WXK_ALT)
        {
            event.Skip();
            return;
        }

        wxShortcutItem item;
        item.SetKeyCode(event.GetKeyCode());
        int flags = 0;
        if (event.CmdDown())
            flags |= wxACCEL_CTRL;
        if (event.AltDown())
            flags |= wxACCEL_ALT;
        if (event.ShiftDown())
            flags |= wxACCEL_SHIFT;
        item.SetFlags(flags);

#if 0
        wxLogDebug(wxT("We pressed: keycode=%d, flags=%d."),
            (int) item.GetKeyCode(), item.GetFlags());
#endif

        wxShortcutItemArray arr = m_shortcutManager->GetMatchingShortcuts(item);
        if (arr.GetCount() > 0)
        {
            // Simulate an accelerator event

            item = arr[0];
            wxCommandEvent cmdEvent(wxEVT_COMMAND_MENU_SELECTED, item.GetId());
            cmdEvent.SetEventObject(m_window);
            if (ProcessEvent(cmdEvent))
                return;
        }
    }
    event.Skip();
}

void AxAdapter::OnChar(wxKeyEvent& event)
{
    // Returns a speakable string for the key pressed
    wxString text = GetSpokenTextForKeycode(event.GetKeyCode());
    if (!text.IsEmpty())
    {
        Say(text, 0 /* AxSpeechFlagsPurge */, AxSpeechVerbosityHigh);
    }

    event.Skip();
}

void AxAdapter::OnIdle(wxIdleEvent& event)
{
    event.Skip();
}

void AxAdapter::OnContextHelp(wxCommandEvent& event)
{
    if (GetMainControl())
    {
        wxHelpEvent helpEvent(wxEVT_HELP, GetMainControl()->GetId(), wxDefaultPosition,
                          wxHelpEvent::Origin_HelpButton);
        helpEvent.SetEventObject(GetMainControl());

        ProcessEvent(helpEvent);
    }
    else
        event.Skip();
}

// propagate the colour change event to the subwindows
void AxAdapter::OnSysColourChanged(wxSysColourChangedEvent& event)
{
    if (GetWindow())
    {
        int hints = event.GetId();
        if (hints == 0)
            hints = AxVisualSettingAll;

        UpdateAppearance(hints);

        wxWindowList::compatibility_iterator node = GetWindow()->GetChildren().GetFirst();
        while ( node )
        {
            // Only propagate to non-top-level windows
            wxWindow *win = node->GetData();
            if ( !win->IsTopLevel() )
            {
                wxSysColourChangedEvent event2;
                event.SetEventObject(win);
                event.SetId(event.GetId()); // a place to store hints
                win->GetEventHandler()->ProcessEvent(event2);
            }
            
            node = node->GetNext();
        }

        PostUpdateAppearance(hints);

        GetWindow()->Refresh();
    }

    // Allow further processing to be done by windows if necessary
    event.Skip();
}

void AxAdapter::OnGoHome(wxCommandEvent& WXUNUSED(event))
{
    if (AxViewManager::Get() && AxViewManager::Get()->GetMenuController())
        AxViewManager::Get()->GetMenuController()->GoHome();
}

void AxAdapter::OnGoUp(wxCommandEvent& WXUNUSED(event))
{
    if (AxViewManager::Get() && AxViewManager::Get()->GetMenuController())
        AxViewManager::Get()->GetMenuController()->GoUp();
}

// Describe current context.
bool AxAdapter::DescribeControlContext(bool metaInfo, AxContentLevel level)
{
    wxString text;

    wxString controlName = GetName();
    wxString controlStateText = GetStateText(level);

    if (metaInfo)
        text << controlName << wxT(".");

    text << wxT(" ") << controlStateText;

    if (level == AxContentFull)
    {
        wxString helpText = GetHelpText();
        if (!helpText.IsEmpty())
        {
            text << wxT(" ") << helpText;
            AddStop(text);
        }
    }

    AddStop(text);

    if (!text.IsEmpty())
        Say(text, AxSpeechFlagsPurge, AxSpeechVerbosityLowest);

    return true;
}

// Describe this window and all its children.
bool AxAdapter::DescribeContainerWindow(AxContentLevel level)
{
    wxString text;

    DescribeWindowRecursively(GetMainControl(), level, text);

    if (!text.IsEmpty())
        Say(text, AxSpeechFlagsPurge, AxSpeechVerbosityLowest);

    return true;
}

// Get descriptions for this window, recursively. Only describe
// visible windows.
bool AxAdapter::DescribeWindowRecursively(wxWindow* window, AxContentLevel level, wxString& text)
{
    AxAdapter* adapter = AxSelfVoicing::FindAdapter(window);
    if (window->IsShownOnScreen() && adapter && adapter->IsVoiced() &&
        (adapter->GetWindow() == adapter->GetMainControl()))
    {
        wxString controlName = adapter->GetName();
        if (!controlName.IsEmpty())
        {
            text << wxT(" ") << controlName;
            AddStop(text);
        }

        if (level == AxContentFull)
        {
            wxString helpText = adapter->GetHelpText();
            if (!helpText.IsEmpty())
            {
                text << wxT(" ") << helpText;
                AddStop(text);
            }
        }

        wxString controlStateText = adapter->GetStateText(level);
        if (!controlStateText.IsEmpty())
        {
            text << wxT(" ") << controlStateText;
            AddStop(text);
        }
    }

    if (window->IsShown())
    {
        wxWindowList::compatibility_iterator node = window->GetChildren().GetFirst();
        while (node)
        {
            wxWindow *child = node->GetData();

            DescribeWindowRecursively(child, level, text);

            node = node->GetNext();
        }
    }

    return true;
}

// Returns the speech channel to be used by this adapter.
AxSpeechChannel AxAdapter::GetChannel(AxSpeechChannel channel) const
{
    if (channel == AxSpeechDefaultChannel)
        channel = m_channel;

    if (channel != AxSpeechMainChannel && channel != AxSpeechContentChannel)
        channel = AxSpeechMainChannel;
    return channel;
}

// Say some text
bool AxAdapter::Say(const wxString& text, int flags, int verbosity)
{
    if (m_speech)
    {
        if (GetChannel() == AxSpeechContentChannel)
            flags |= AxSpeechFlagsContentChannel;

        AxSpeechEvent speechEvent;
        speechEvent.SetSpeechEventType(AxSpeechEventText);
        speechEvent.SetVerbosityLevel(verbosity);
        speechEvent.SetText(text);
        speechEvent.SetFlags(flags);
        GetSpeech()->EmitSpeech(speechEvent);
        return true;
    }
    else
        return false;
}

// Say some text
bool AxAdapter::Say(AxSpeechChannel channel, const wxString& text, int flags, int verbosity)
{
    if (GetChannel(channel) == AxSpeechContentChannel)
        flags |= AxSpeechFlagsContentChannel;
    return Say(text, flags, verbosity);
}

bool AxAdapter::StopSpeech(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (channel == AxSpeechAllChannels)
        {
            GetSpeech()->Stop(AxSpeechMainChannel);
            GetSpeech()->Stop(AxSpeechContentChannel);
        }
        else if (channel == AxSpeechDefaultChannel)
            GetSpeech()->Stop(); // Tries one, then the other channel
        else
            GetSpeech()->Stop(GetChannel(channel));

        return true;
    }
    else
        return false;
}

// Pause or resume speech
bool AxAdapter::PauseOrResumeSpeech(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (channel == AxSpeechAllChannels)
        {
            GetSpeech()->PauseOrResume(AxSpeechMainChannel);
            GetSpeech()->PauseOrResume(AxSpeechContentChannel);
        }
        else if (channel == AxSpeechDefaultChannel)
            GetSpeech()->PauseOrResume();
        else
            GetSpeech()->PauseOrResume(GetChannel(channel));
        return true;
    }
    else
        return false;
}

// Are we speaking?
bool AxAdapter::IsSpeaking(AxSpeechChannel channel) const
{
    return GetSpeech() && GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsSpeaking();
}

// Can we stop?
bool AxAdapter::CanStop(AxSpeechChannel channel) const
{
    return GetSpeech() && (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsSpeaking() || GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPaused());
}

// Can we pause?
bool AxAdapter::CanPause(AxSpeechChannel channel) const
{
    return GetSpeech() && (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsSpeaking() && !GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPaused());
}

// Can we resume?
bool AxAdapter::CanResume(AxSpeechChannel channel) const
{
    return GetSpeech() && GetSpeech()->GetTextToSpeech(GetChannel(GetChannel(channel))).IsPaused();
}

// Skip forward
bool AxAdapter::SkipForward(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPlaying() && GetSpeech()->GetTextToSpeech(GetChannel(channel)).HasCapability(wxTTS_CAP_SKIP))
            GetSpeech()->GetTextToSpeech(GetChannel(channel)).Skip(1);
        return true;
    }
    else
        return false;
}

// Skip forward
bool AxAdapter::SkipBack(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPlaying() && GetSpeech()->GetTextToSpeech(GetChannel(channel)).HasCapability(wxTTS_CAP_SKIP))
            GetSpeech()->GetTextToSpeech(GetChannel(channel)).Skip(-1);
        return true;
    }
    else
        return false;
}

bool AxAdapter::IsPunctuation(const wxChar& ch)
{
    wxString p(wxT(".,;:!?\"'"));
    return (p.Find(ch) != wxNOT_FOUND);
}

bool AxAdapter::IsWhitespace(const wxChar& ch)
{
    wxString p(wxT(" \n\t"));
    return (p.Find(ch) != wxNOT_FOUND);
}

bool AxAdapter::IsSentenceEnd(const wxChar& ch)
{
    wxString p(wxT(".!?"));
    return (p.Find(ch) != wxNOT_FOUND);
}

// Remove trailing punctuation
bool AxAdapter::RemoveTrailingPunctuation(wxString& str)
{
    return AxSpeechEventHandler::RemoveTrailingPunctuation(str);
}

// Add a full stop to the string, unless there's already one there.
// Remove extra punctuation, e.g. semicolon.
bool AxAdapter::AddStop(wxString& str, const wxString& punct)
{
    return AxSpeechEventHandler::AddStop(str, punct);
}

// Add a semicolon to the string to create a pause, unless there's already one there.
// Remove extra punctuation.
bool AxAdapter::AddPause(wxString& str, const wxString& punct)
{
    return AxSpeechEventHandler::AddPause(str, punct);
}

// Returns a speakable string for the key pressed
wxString AxAdapter::GetSpokenTextForKeycode(int keycode)
{
    return AxGetSpeakableTextForKeyCode(keycode);
}

// Find a static text control that might describe this control.
wxString AxAdapter::GetStaticLabel()
{
    wxWindow* window = GetMainControl();
    if (window && window->GetParent())
    {
        wxWindowList::compatibility_iterator node = window->GetParent()->GetChildren().Find(window);
        if (node && node->GetPrevious())
        {
            wxStaticText* staticText = wxDynamicCast(node->GetPrevious()->GetData(), wxStaticText);
            if (staticText)
            {
                if (staticText->IsShownOnScreen())
                    return wxStripMenuCodes(staticText->GetLabel());
            }
        }
    }
    return wxEmptyString;
}

// Get a possible label using wxControl::GetLabel
wxString AxAdapter::GetControlLabel()
{
    if (!m_alternateLabel.IsEmpty())
        return m_alternateLabel;

    wxString label;
    wxControl* control = wxDynamicCast(GetMainControl(), wxControl);
    if (control)
        label = control->GetLabel();
    else if (control != GetWindow())
    {
        // If it's a composite control and the main control doesn't yield a label,
        // try the child control.
        wxControl* control2 = wxDynamicCast(GetWindow(), wxControl);
        if (control2)
            label = control2->GetLabel();
    }
    return label;
}

// Override to find a static text label for the control.
wxString AxAdapter::GetLabel()
{
    return GetStaticLabel();
}

// Override to return help on the current control.
// By default, wxWindow::GetHelpText is used.
wxString AxAdapter::GetHelpText()
{
    if (GetMainControl())
        return GetMainControl()->GetHelpText();
    else
        return wxEmptyString;
}

// Get the AxAdapter for the focused control,
// or if not found, return 'this'.
AxAdapter* AxAdapter::FindFocusedAdapter()
{
    wxWindow* win = wxWindow::FindFocus();
    if (win)
    {
        AxAdapter* adapter = AxSelfVoicing::FindAdapter(win);
        if (adapter)
            return adapter;
    }
    return this;
}

void AxAdapter::OnDescribeContext(wxCommandEvent& WXUNUSED(event))
{
    FindFocusedAdapter()->DescribeControlContext(true, AxContentFull);
}

void AxAdapter::OnDescribeContainer(wxCommandEvent& WXUNUSED(event))
{
    wxWindow* win = GetMainControl();
    AxAdapter* lastAdapter = this;
    while (win)
    {
        AxAdapter* adapter = AxSelfVoicing::FindAdapter(win);
        if (adapter && (adapter->GetWindow() == adapter->GetMainControl()))
            lastAdapter = adapter;

        if (win->IsTopLevel())
            break;

        win = win->GetParent();
    }
    AxAdapter* adapter = lastAdapter;
    adapter->DescribeContainerWindow(AxContentSummary);
}

void AxAdapter::OnStopSpeech(wxCommandEvent& WXUNUSED(event))
{
    FindFocusedAdapter()->StopSpeech();
}

void AxAdapter::OnPauseResumeSpeech(wxCommandEvent& WXUNUSED(event))
{
    FindFocusedAdapter()->PauseOrResumeSpeech();
}

void AxAdapter::OnSkipForward(wxCommandEvent& WXUNUSED(event))
{
    FindFocusedAdapter()->SkipForward();
}

void AxAdapter::OnSkipBack(wxCommandEvent& WXUNUSED(event))
{
    FindFocusedAdapter()->SkipBack();
}

void AxAdapter::OnSetFocus(wxFocusEvent& event)
{
    event.Skip();

    // Hide any help window that's showing
    AxHelpProvider::CleanUp();

    DescribeControlContext(true, AxContentSummary);
}

// Apply the current font size to the window
bool AxAdapter::ApplyFontSize()
{
    if (AxViewManager::Get())
    {
#if 0
        if (GetMainControl())
        {
            wxFont font(GetMainControl()->GetFont());
            font.SetPointSize(AxViewManager::Get()->GetVisualSettings().GetBestTextSize());
            GetMainControl()->SetFont(font);
        }
#endif
        if (GetWindow()) //  && (GetWindow() != GetMainControl()))
        {
            wxFont font(GetWindow()->GetFont());
            font.SetPointSize(AxViewManager::Get()->GetVisualSettings().GetBestTextSize());
            GetWindow()->SetFont(font);
        }
    }
    return true;
}

// Apply window colours
bool AxAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            // If we don't always call this, then window colours may not get
            // reset to the default system colour (needs restart).
            // However, setting it always can cause occasional visual problems.
            //if (AxViewManager::Get()->GetVisualSettings().GetWindowColour().Ok())
                GetWindow()->SetBackgroundColour(AxViewManager::Get()->GetVisualSettings().GetBestWindowColour());
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());

        }
    }

    return true;
}

// Update the appearance - text size, background, etc.
bool AxAdapter::UpdateAppearance(int hints)
{
    if (AxViewManager::Get() && GetMainControl())
    {
        if (hints & AxVisualSettingTextSize)
        {
            ApplyFontSize();
        }

        if (hints & (AxVisualSettingWindowColour|AxVisualSettingPanelColour))
        {
            ApplyWindowColours();
        }
    }

    return true;
}

IMPLEMENT_CLASS(AxVoiceAdaptationHandler, wxObject)

// Override to adapt e.g. a composite control.
bool AxVoiceAdaptationHandler::Adapt(wxWindow* window, AxSpeech* speech, wxShortcutManager* shortcuts, wxArrayInt& ids)
{
    AxAdapter* evtHandler = CreateEventHandler(window);
    if (evtHandler)
    {
        evtHandler->SetWindow(window);
        if (speech)
            evtHandler->SetSpeech(speech);
        if (shortcuts)
            evtHandler->SetShortcutManager(shortcuts);

        ids.Add(window->GetId());
        
        window->PushEventHandler(evtHandler);

        evtHandler->UpdateAppearance(AxVisualSettingAll);

        return true;
    }
    return false;
}

// Override to unadapt e.g. a composite control.
bool AxVoiceAdaptationHandler::Unadapt(wxWindow* window, wxArrayInt& ids)
{
    AxAdapter* handler = AxSelfVoicing::FindAdapter(window);
    if (handler && ids.Index(window->GetId()) != wxNOT_FOUND)
    {
        window->RemoveEventHandler(handler);
        delete handler;
        ids.Remove(window->GetId());
    }

    return true;
}

IMPLEMENT_DYNAMIC_CLASS(AxSelfVoicing, wxEvtHandler)

BEGIN_EVENT_TABLE(AxSelfVoicing, wxEvtHandler)
END_EVENT_TABLE()

AxVoiceAdaptationHandlerArray AxSelfVoicing::m_voiceAdaptationHandlers;

AxSelfVoicing::AxSelfVoicing(wxWindow* window, AxSpeech* speech, wxShortcutManager* shortcutManager)
{
    Init();

    m_window = window;
    if (speech)
        m_speech = speech;
    if (shortcutManager)
        m_shortcutManager = shortcutManager;
}

void AxSelfVoicing::Init()
{
    m_window = NULL;
    m_adapted = false;

    SetShortcutManager(AxViewManager::Get()->GetShortcutManager());
    SetSpeech(& AxViewManager::Get()->GetSpeech());
}

AxSelfVoicing::~AxSelfVoicing()
{
    UndoAdaptation();
}

// Adapt the controls in this window.
bool AxSelfVoicing::Adapt(wxWindow* window, int flags)
{
    if (window)
        SetWindow(window);

    if (IsAdapted() || !m_window)
        return false;

    m_adaptedCtrls.Clear();

    AdaptWindow(m_window, false);

    wxWindowList::compatibility_iterator node = m_window->GetChildren().GetFirst();
    while (node)
    {
        wxWindow *child = node->GetData();
        AdaptWindow(child, true);
        node = node->GetNext();
    }

    if ((flags & AxAdaptSetAccelerators) && GetShortcutManager())
    {
        // Warning: this can cause shortcuts to become unresponsive
        GetShortcutManager()->SetAcceleratorTable(m_window);
    }

    m_adapted = true;

    return true;
}

// Restore the controls in this window.
bool AxSelfVoicing::UndoAdaptation()
{
    if (!IsAdapted() || !m_window)
        return false;

    UnadaptWindow(m_window, false);

    wxWindowList::compatibility_iterator node = m_window->GetChildren().GetFirst();
    while (node)
    {
        wxWindow *child = node->GetData();
        UnadaptWindow(child, true);
        node = node->GetNext();
    }

    m_adapted = false;

    return true;
}

// Adapt a single window, optionally recursing.
bool AxSelfVoicing::AdaptWindow(wxWindow* window, bool recurse)
{
    bool canRecurse = recurse;
    if (!FindAdapter(window))
    {
        AxVoiceAdaptationHandler* handler = FindAdaptationHandler(window);
        if (handler)
        {
            handler->Adapt(window, GetSpeech(), GetShortcutManager(), m_adaptedCtrls);

            if (!handler->CanRecursivelyAdapt(window))
                canRecurse = false;
        }
    }

    if (canRecurse)
    {
        wxWindowList::compatibility_iterator node = window->GetChildren().GetFirst();
        while (node)
        {
            wxWindow *child = node->GetData();
            AdaptWindow(child, recurse);
            node = node->GetNext();
        }
    }
    return true;
}

// Unadapt a single window, optionally recursing.
bool AxSelfVoicing::UnadaptWindow(wxWindow* window, bool recurse)
{
    bool canRecurse = recurse;
    AxVoiceAdaptationHandler* adaptationHandler = FindAdaptationHandler(window);
    if (adaptationHandler)
    {
        adaptationHandler->Unadapt(window, m_adaptedCtrls);
        if (!adaptationHandler->CanRecursivelyAdapt(window))
            canRecurse = false;
    }

    if (canRecurse)
    {
        wxWindowList::compatibility_iterator node = window->GetChildren().GetFirst();
        while (node)
        {
            wxWindow *child = node->GetData();
            UnadaptWindow(child, recurse);
            node = node->GetNext();
        }
    }
    return true;
}


// Clears the handlers
void AxSelfVoicing::ClearAdaptationHandlers()
{
    size_t i;
    for (i = 0; i < m_voiceAdaptationHandlers.GetCount(); i++)
    {
        AxVoiceAdaptationHandler* handler = m_voiceAdaptationHandlers[i];
        delete handler;
    }
    m_voiceAdaptationHandlers.Clear();
}

// Finds a handler for the given window
AxVoiceAdaptationHandler* AxSelfVoicing::FindAdaptationHandler(wxWindow* window)
{
    size_t i;
    for (i = 0; i < m_voiceAdaptationHandlers.GetCount(); i++)
    {
        AxVoiceAdaptationHandler* handler = m_voiceAdaptationHandlers[i];
        if (handler->CanHandleWindow(window))
            return handler;
    }
    return NULL;
}

// Adds standard handlers
void AxSelfVoicing::AddStandardAdaptationHandlers()
{
    AddAdaptationHandler(new AxStandardControlsVoiceAdaptationHandler);
    AddAdaptationHandler(new AxTextCtrlVoiceAdaptationHandler);
}

// Finds a voice event handler in the given window's event stack
AxAdapter* AxSelfVoicing::FindAdapter(wxWindow* window)
{
    wxEvtHandler *handler = window->GetEventHandler();
    while ( handler )
    {
        AxAdapter* voicedHandler = wxDynamicCast(handler, AxAdapter);
        if (voicedHandler)
            return voicedHandler;

        handler = handler->GetNextHandler();
    }

    return NULL;
}

bool AxSelfVoicing::AddAdapter(wxWindow* window, AxAdapter* adapter)
{
    adapter->SetWindow(window);
    window->PushEventHandler(adapter);
    adapter->UpdateAppearance(AxVisualSettingAll);
    return true;
}

bool AxSelfVoicing::RemoveAdapter(wxWindow* window, AxAdapter* adapter)
{
    if (!adapter)
        adapter = AxSelfVoicing::FindAdapter(window);

    if (adapter)
    {
        window->RemoveEventHandler(adapter);
        delete adapter;
        return true;
    }
    else
        return false;
}

// A module to initialize the built-in control handlers.

class AxSelfVoicingModule: public wxModule
{
DECLARE_DYNAMIC_CLASS(AxSelfVoicingModule)
public:
    AxSelfVoicingModule() {}
    bool OnInit() { AxSelfVoicing::AddStandardAdaptationHandlers(); return true; }
    void OnExit() { AxSelfVoicing::ClearAdaptationHandlers(); }
};

IMPLEMENT_DYNAMIC_CLASS(AxSelfVoicingModule, wxModule)

